#include "tplexpressionparser.h"
#include "tplgetexpression.h"
#include <QScopedPointer>
#include <QStack>

enum TPL_EXPR_STATE {
    TPL_EXPR_STATE_ANY_CHAR,
    TPL_EXPR_STATE_ASSIGN,
    TPL_EXPR_STATE_ADD,
    TPL_EXPR_STATE_SUB,
    TPL_EXPR_STATE_MUL,
    TPL_EXPR_STATE_DIV,
    TPL_EXPR_STATE_MOD,
    TPL_EXPR_STATE_EQUALS_TO,
    TPL_EXPR_STATE_NOT_EQUALS_TO,
    TPL_EXPR_STATE_GT,
    TPL_EXPR_STATE_LT,
    TPL_EXPR_STATE_GT_EQ,
    TPL_EXPR_STATE_LT_EQ,
    TPL_EXPR_STATE_NOT,
    TPL_EXPR_STATE_AND,
    TPL_EXPR_STATE_OR,
    TPL_EXPR_STATE_ARRAY_START,
    TPL_EXPR_STATE_ARRAY_END,
    TPL_EXPR_STATE_BRACKET_START,
    TPL_EXPR_STATE_BRACKET_END,
    TPL_EXPR_STATE_DOT
};

typedef struct TplParserState {
    int lines;
    int bytes;

    TPL_EXPR_STATE state;
    TPL_EXPR_STATE prevState;
    char c;
    char prevChar;
    QByteArray currentBuffer;
    QStack<TplExpression*> expressions;
    QMap<QString, QVariant> *context;

    TplParserState() {}
    ~TplParserState() {
#ifdef TPL_DEBUG
        qDebug() << "Deleting object TplParserState: " << this << endl;
#endif
    }

} TplParserState;

static bool handleEqual(TplParserState* state);
static bool handleAdd(TplParserState* state);
static bool handleSub(TplParserState* state);
static bool handleMul(TplParserState* state);
static bool handleDiv(TplParserState* state);
static bool handleMod(TplParserState* state);
static bool handleExclMark(TplParserState* state);
static bool handleGt(TplParserState* state);
static bool handleLt(TplParserState* state);
static bool handleAmp(TplParserState* state);
static bool handleOpenSqBracket(TplParserState* state);
static bool handleCloseSqBracket(TplParserState* state);
static bool handleSingleQuote(TplParserState* state);
static bool handleOpenBracket(TplParserState* state);
static bool handleCloseBracket(TplParserState* state);
static bool handleDot(TplParserState* state);
static bool handleText(TplParserState* state);


TplExpressionParser::TplExpressionParser(QObject *parent):TplParser(parent) {
}

TplExpressionParser::~TplExpressionParser() {
#ifdef TPL_DEBUG
    qDebug() << "Deleting object TplExpressionParser: " << this << endl;
#endif
}

void TplExpressionParser::setContext(QMap<QString, QVariant> *context) {
    this->context = context;
}

QVariant TplExpressionParser::parse(QIODevice *inputSource, bool *failed) {

    QScopedPointer<TplExpression> finalExpression;
    QScopedPointer<TplParserState> parserState(new TplParserState());
    parserState->state = parserState->prevState = TPL_EXPR_STATE_ANY_CHAR;
    parserState->lines = parserState->bytes = 0;
    parserState->context = this->context;

    while(inputSource->read(&parserState->c, sizeof(parserState->c)) != -1) {

        switch(parserState->c) {
        case '=':
            if(handleEqual(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '+':
            if(handleAdd(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '-':
            if(handleSub(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '*':
            if(handleMul(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '/':
            if(handleDiv(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '%':
            if(handleMod(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '!':
            if(handleExclMark(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '>':
            if(handleGt(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '<':
            if(handleLt(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '&':
            if(handleAmp(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '[':
            if(handleOpenSqBracket(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case ']':
            if(handleCloseSqBracket(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '\'':
            if(handleSingleQuote(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '(':
            if(handleOpenBracket(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case ')':
            if(handleCloseBracket(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        case '.':
            if(handleDot(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        default:
            if(handleText(parserState.data()) == false) {
                *failed = false;
                return 0;
            }
            break;
        }

        if(parserState->c == '\n') {
            ++ parserState->lines;
            parserState->bytes = 0;
        } else {
            ++ parserState->bytes;
        }
    }

    return 0 ;
}


static bool handleEqual(TplParserState* state) {
    switch(state->state) {
    case TPL_EXPR_STATE_ANY_CHAR:
        if(state->currentBuffer.size() > 0) {
            QScopedPointer<TplGetExpression> leftOperator(new TplGetExpression());
            leftOperator->setContext(state->context);
            leftOperator->setValue(QVariant(QString(state->currentBuffer)));
            state->prevChar = state->c;
            state->currentBuffer.clear();
            state->prevState = state->state;
            state->state = TPL_EXPR_STATE_ASSIGN;

            state->expressions.push(leftOperator.take());
        } else {
            qFatal("No left operator for assignament or equals at line: %d, byte: %d",state->lines, state->bytes);
            return false;
        }

        break;

    case TPL_EXPR_STATE_ASSIGN:
        state->prevState = state->state;
        state->state = TPL_EXPR_STATE_EQUALS_TO;
        break;

    case TPL_EXPR_STATE_ADD:
    case TPL_EXPR_STATE_SUB:
    case TPL_EXPR_STATE_MUL:
    case TPL_EXPR_STATE_DIV:
    case TPL_EXPR_STATE_MOD:
    case TPL_EXPR_STATE_EQUALS_TO:
    case TPL_EXPR_STATE_NOT_EQUALS_TO:
    case TPL_EXPR_STATE_GT_EQ:
    case TPL_EXPR_STATE_LT_EQ:
    case TPL_EXPR_STATE_AND:
    case TPL_EXPR_STATE_OR:
    case TPL_EXPR_STATE_ARRAY_END:
    case TPL_EXPR_STATE_BRACKET_START:
    case TPL_EXPR_STATE_BRACKET_END:
    case TPL_EXPR_STATE_DOT:
    case TPL_EXPR_STATE_ARRAY_START:

        qFatal("Unexpected character = at line: %d, byte: %d",state->lines, state->bytes);
        return false;

    case TPL_EXPR_STATE_GT:
        state->prevState = state->state;
        state->state = TPL_EXPR_STATE_GT_EQ;
        break;
    case TPL_EXPR_STATE_LT:
        state->prevState = state->state;
        state->state = TPL_EXPR_STATE_LT_EQ;
        break;
    case TPL_EXPR_STATE_NOT:
        state->prevState = state->state;
        state->state = TPL_EXPR_STATE_NOT_EQUALS_TO;
        break;
    }

    return true;
}

static bool handleAdd(TplParserState* state) {
    return true;
}

static bool handleSub(TplParserState* state) {
    return true;
}

static bool handleMul(TplParserState* state) {
    return true;
}

static bool handleDiv(TplParserState* state) {
    return true;
}

static bool handleMod(TplParserState* state) {
    return true;
}

static bool handleExclMark(TplParserState* state) {
    return true;
}

static bool handleGt(TplParserState* state) {
    return true;
}

static bool handleLt(TplParserState* state) {
    return true;
}

static bool handleAmp(TplParserState* state) {
    return true;
}

static bool handleOpenSqBracket(TplParserState* state) {
    return true;
}

static bool handleCloseSqBracket(TplParserState* state) {
    return true;
}

static bool handleSingleQuote(TplParserState* state) {
    return true;
}

static bool handleOpenBracket(TplParserState* state) {
    return true;
}

static bool handleCloseBracket(TplParserState* state) {
    return true;
}

static bool handleDot(TplParserState* state) {
    return true;
}

static bool handleText(TplParserState* state) {
    switch(state->state) {
    case TPL_EXPR_STATE_ANY_CHAR:
        state->currentBuffer.append(state->c);
        state->prevChar = state->c;
        state->prevState = TPL_EXPR_STATE_ANY_CHAR;
        break;
    }

    return true;
}
